// Copyright (C) 2012 Yury Bychenkov (http://bychenkov.com).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//  - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//  - Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//  - Neither the name of Yury Bychenkov nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

inline Thread::Thread(): 
	threadHandle(0), 
	interrupted(false) 
{
}

inline void Thread::interrupt() {
	interrupted = true;
}

inline bool Thread::isInterrupted() {
	return interrupted;
}

#ifdef WINDOWS64

inline void Thread::sleep(uint32 millis) {
	::Sleep(millis);
}

inline void Thread::yield() {
	::Sleep(0);
}

inline LatchLock::LatchLock() {
	::InitializeCriticalSection(&criticalSection);
}

inline LatchLock::~LatchLock() {
	::DeleteCriticalSection(&criticalSection);
}

inline void LatchLock::lock() {
	::EnterCriticalSection(&criticalSection);
}

inline void LatchLock::unlock() {
	::LeaveCriticalSection(&criticalSection);
}

#endif // WINDOWS64


#ifdef POSIX64

inline void Thread::sleep(uint32 millis) {
	::usleep(useconds_t(millis) * 1000);
}

inline void Thread::yield() {
	::sched_yield();
}

inline LatchLock::LatchLock(): locked(0) {   
}

inline LatchLock::~LatchLock() {
}

inline void LatchLock::lock() {
    while (true) {
	    uint32 prev;

	    asm volatile (
   	 		"movl $1, %%eax;"
		    "xchg %%eax, %0;"
    		"movl %%eax, %1;"
	    	: "=m" (locked), "=m" (prev)
    		:
	    	: "%eax", "memory"
    	);

    	if (prev == 0) {
	    	asm volatile("mfence" ::: "memory");
    		return;
    	}

    	Thread::yield();
    }
}

inline void LatchLock::unlock() {
    asm volatile (
    	"mfence;"
    	"movl $0, %%eax;"
    	"xchg %%eax, %0;"
    	: "=m" (locked)
    	:
    	: "%eax", "memory"
    );
}

#endif // POSIX64




inline ReadWriteLatch::ReadWriteLatch(): readLockCount(0), awaitingWriterCount(0) {
}

inline void ReadWriteLatch::readLock() {
	while (true) {
		lock.lock();
		if (awaitingWriterCount == 0) {
			readLockCount++;
			lock.unlock();
			break;
		}
		lock.unlock();
		Thread::yield();
    }
}

inline void ReadWriteLatch::readUnlock() {
	lock.lock();
	readLockCount--;
	lock.unlock();
}

inline void ReadWriteLatch::writeLock() {
	lock.lock();
	if (readLockCount == 0) {
		return;
	}
	awaitingWriterCount++;
	lock.unlock();

	while (true) {
		Thread::yield();

		lock.lock();
		if (readLockCount == 0) {
			awaitingWriterCount--;
			return;
		}
		lock.unlock();
   }
}

inline void ReadWriteLatch::writeUnlock() {
	lock.unlock();
}

template <typename Lock>
LockGuard<Lock>::LockGuard(Lock& lock_): lock(lock_) {
	lock.lock();
}

template <typename Lock>
LockGuard<Lock>::~LockGuard() {
	lock.unlock();
}


template <typename T>
SafeArray<T>::SafeArray() {
}

template <typename T>
SafeArray<T>::SafeArray(const vector<T>& data_):
	data(data_)
{
}

template <typename T>
SafeArray<T>::SafeArray(const SafeArray& data_)
{
	LockGuard<LatchLock> guard(data_.lock);
	data = data_.data;
}

template <typename T>
uint SafeArray<T>::getCount() const {
	LockGuard<LatchLock> guard(lock);
	return data.size();
}

template <typename T>
uint SafeArray<T>::add(const T& e) {
	LockGuard<LatchLock> guard(lock);
	data.push_back(e);
	return data.size() - 1;
}

template <typename T>
T SafeArray<T>::operator[](uint index) const {
	LockGuard<LatchLock> guard(lock);
	T t = data[index];
	return t;
}

template <typename T>
bool SafeArray<T>::get(uint index, T& e) const {
	LockGuard<LatchLock> guard(lock);
	if (index < data.size()) {
		e = data[index];
		return true;
	}
	return false;
}

template <typename T>
bool SafeArray<T>::set(uint index, const T& e) {
	LockGuard<LatchLock> guard(lock);
	if (index < data.size()) {
		data[index] = e;
		return true;
	}
	return false;
}

template <typename T>
bool SafeArray<T>::getLast(T& e) const {
	LockGuard<LatchLock> guard(lock);
	if (data.size() > 0) {
		e = data[data.size() - 1];
		return true;
	}
	return false;
}
